package deepbrain.simnetwork.mechanism;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import deepbrain.simnetwork.network.Network;
import deepbrain.simnetwork.network.NetworkState;
import deepbrain.simnetwork.network.NodeState;
import deepbrain.simnetwork.structure.Connection;

public class NeuralMechanism extends GenericMechanism {

	private double threshold = 0;

	public NeuralMechanism() {
	}
	
	public NeuralMechanism(double threshold) {
		this.threshold = threshold;
	}

	private Map<Double, Double> getConvolutionProbability(
			List<Map<Double, Double>> afferentNodes) {
		Map<Double, Double> result = new HashMap<Double, Double>();
		result.put(0.0, 1.0);
		for(Map<Double, Double> stim:afferentNodes)
			result = convolution(result, stim);
		return result;
	}

	private Map<Double, Double> convolution(Map<Double, Double> node1,
			Map<Double, Double> node2) {
		Map<Double, Double> result = new HashMap<Double, Double>();
		for (Map.Entry<Double, Double> stim1 : node1.entrySet()) {
			for (Map.Entry<Double, Double> stim2 : node2.entrySet()) {
				addProbabilityToMap(result, stim1.getKey() + stim2.getKey(),
						stim1.getValue() * stim2.getValue());
			}
		}
		return result;
	}

	private void addProbabilityToMap(Map<Double, Double> map, double value,
			double prob) {
		Double _preProb = map.get(value);
		double preProb;
		if (_preProb == null)
			preProb = 0;
		else
			preProb = _preProb;
		map.put(value, preProb + prob);
	}
	
	@SuppressWarnings("unchecked")
	public NodeState nextNodeState(int node, Network network, NetworkState state) {
		List<Map<Double,Double>> afferents = new ArrayList<Map<Double,Double>>();
		for (Connection<?> _connection : network.getAfferentConnections(node)) {
			Connection<Double> connection = (Connection<Double>) _connection;
			double fireProb = state.getNodeState(connection.source).getFiringProbability();
			HashMap<Double,Double> probs = new HashMap<Double, Double>();
			if (fireProb!=0.0)
				probs.put(connection.getWeight(), fireProb);
			if (fireProb!=1.0)
				probs.put(0.0, 1-fireProb);
			afferents.add(probs);
		}
		Map<Double,Double> result = getConvolutionProbability(afferents);
//		System.out.println("Afferents: "+afferents);
//		System.out.println("Result: "+result);
		double sum=0.0;
		for(Map.Entry<Double, Double> entry:result.entrySet()) {
			if (entry.getKey()>=threshold)
				sum+=entry.getValue();
		}
		return NodeState.newNodeState(sum);
	}

	@Override
	public String toString() {
		return "Neural Mechanism";
	}
}
